/**
 * Copyright 2021 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef EXECUTE_GRAPH_SMALL_VECTOR_H
#define EXECUTE_GRAPH_SMALL_VECTOR_H
#include <array>
#include <iterator>
#include <memory>
#include <vector>
// TODO 改造具有constexpr的能力
template<typename T, size_t N = 8>
class SmallVector {
 public:
  using iterator = T *;
  using const_iterator = const T *;
  using reference = T &;
  using const_reference = const T &;
  using pointer = T *;
  using const_pointer = const T *;

  size_t size() const noexcept {
    return size_;
  }

  iterator begin() noexcept {
    return data();
  }

  iterator end() noexcept {
    return data() + size_;
  }

  const_iterator begin() const noexcept {
    return data();
  }

  const_iterator end() const noexcept {
    return data() + size_;
  }

  reference operator[](size_t i) {
    return data()[i];
  }
  const_reference operator[](size_t i) const {
    return data()[i];
  }

  reference at(size_t i) {
    return data()[i];
  }
  const_reference at(size_t i) const {
    return data()[i];
  }

  bool empty() const noexcept {
    return size_ == 0;
  }

  size_t capacity() const noexcept {
    return cap_;
  }

  void resize(size_t count) {
    /*
     * count < size_: deconstruct
     * size < count < cap_: easy
     * cap_ < count: alloc from heap, and move objs on to it.
     */
    if (count < size()) {
      for (size_t i = count; i < size_; ++i) {
        data()[i]->~T();
      }
      size_ = count;
      cap_ = count;
    } else if (count <= cap_) {
      for (size_t i = size(); i < count; ++i) {
        data()[i]->T();
      }
      cap_ = count;
    } else {
      if (count <= N) {
        for (size_t i = 0; i < count; ++i) {
          data()[i]->T();
        }
      } else {
        auto p = std::unique_ptr<T[]>(new (std::nothrow) T[count]);
        for (size_t i = 0; i < size(); ++i) {
          p[i]->T(std::move(data()[i]));
        }
        dynamic_vec_.swap(p);
      }
      cap_ = count;
      size_ = count;
    }
  }

  const_pointer data() const noexcept {
    return dynamic_vec_ == nullptr ? static_vec_.data() : dynamic_vec_.get();
  }
  pointer data() noexcept {
    return dynamic_vec_ == nullptr ? static_vec_.data() : dynamic_vec_.get();
  }

 private:
  size_t size_{0};
  size_t cap_{N};
  std::array<T, N> static_vec_;
  std::unique_ptr<T[]> dynamic_vec_{nullptr};
};

template<typename T>
void swap(typename SmallVector<T>::iterator &iter1, typename SmallVector<T>::iterator &iter2) {
  iter1.swap(iter2);
}

template<typename T>
typename SmallVector<T>::SmallVectorIterator
operator+(typename SmallVector<T>::SmallVectorIterator iter,
          typename SmallVector<T>::SmallVectorIterator::difference_type n) {
  return iter += n;
}

template<typename T>
typename SmallVector<T>::SmallVectorIterator operator+(typename SmallVector<T>::SmallVectorIterator::difference_type n,
                                                       typename SmallVector<T>::SmallVectorIterator iter) {
  return iter += n;
}

template<typename T>
typename SmallVector<T>::SmallVectorIterator
operator-(typename SmallVector<T>::SmallVectorIterator iter,
          typename SmallVector<T>::SmallVectorIterator::difference_type n) {
  return iter -= n;
}

template<typename T>
typename SmallVector<T>::SmallVectorIterator operator-(typename SmallVector<T>::SmallVectorIterator::difference_type n,
                                                       typename SmallVector<T>::SmallVectorIterator iter) {
  return iter -= n;
}

template<typename T>
typename SmallVector<T>::SmallVectorIterator::difference_type
operator-(typename SmallVector<T>::SmallVectorIterator i1, typename SmallVector<T>::SmallVectorIterator i2) {
  return i1.p_ - i2.p_;
}
#endif  //EXECUTE_GRAPH_SMALL_VECTOR_H
